﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Linq.Expressions;
using System.Reflection;

namespace Dynamic
{
    public class Constructor
    {
        public Func<object[],object> Creator;
        private Constructor(Func<object[], object> func)
        {
            Creator= func;
        }
        public static Constructor Create(Type objType,params Type[] paramType)
        {
            Expression[] innerArgs = new Expression[paramType.Length];
            ParameterExpression arrArg = Expression.Parameter(typeof(object[]),"parameters");
            for (int i=0;i<paramType.Length;i++)
            {
                var objT=typeof(object);
                if (paramType[i].IsValueType)
                {
                    innerArgs[i] = Expression.Unbox(Expression.ArrayIndex(arrArg,Expression.Constant(i)), paramType[i]);
                }
                else
                {
                    innerArgs[i] = Expression.TypeAs(Expression.ArrayIndex(arrArg, Expression.Constant(i)), paramType[i]);
                }
            }
            var finalExp = Expression.New(objType.GetConstructor(paramType), innerArgs);
            var exp=Expression.Lambda<Func<object[], object>>(finalExp,arrArg);
            return new Constructor(exp.Compile());
        }
        public T GetInstance<T>(params object[] args)
        {
            return (T)Creator(args);
        }
    }
    public class InstanceCreator<Tobj>
    {
        public Func<object[], Tobj> Creator;
        public Func<Tobj> NoParamsCreator;
        private InstanceCreator(Func<object[], Tobj> func)
        {
            Creator = func;
        }
        private InstanceCreator(Func<Tobj> func)
        {
            NoParamsCreator = func;
        }
        public static InstanceCreator<Tobj> Create(params Type[] paramType)
        {
            Expression[] innerArgs = new Expression[paramType.Length];
            ParameterExpression arrArg = Expression.Parameter(typeof(object[]), "parameters");
            for (int i = 0; i < paramType.Length; i++)
            {
                var objT = typeof(object);
                if (paramType[i].IsValueType)
                {
                    innerArgs[i] = Expression.Unbox(Expression.ArrayIndex(arrArg, Expression.Constant(i)), paramType[i]);
                }
                else
                {
                    innerArgs[i] = Expression.TypeAs(Expression.ArrayIndex(arrArg, Expression.Constant(i)), paramType[i]);
                }
            }
            var finalExp = Expression.New(typeof(Tobj).GetConstructor(paramType), innerArgs);
            var exp = Expression.Lambda<Func<object[], Tobj>>(finalExp, arrArg);
            return new InstanceCreator<Tobj>(exp.Compile());
        }
        public static InstanceCreator<Tobj> Create()
        {
            var finalExp = Expression.New(typeof(Tobj).GetConstructor(new Type[]{ }));
            var exp = Expression.Lambda<Func<Tobj>>(finalExp);
            return new InstanceCreator<Tobj>(exp.Compile());
        }
        public Tobj GetInstance(params object[] args)
        {
            return Creator(args);
        }
        public Tobj GetInstance()
        {
            return NoParamsCreator();
        }
    }
}
